// https://leetcode-cn.com/problems/lru-cache/
// 146. LRU 缓存
// 请设计并实现一个满足 LRU（最近最少使用）缓存约束的数据结构。
// 实现 LRUCache 类：
// - LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
// - int get(int key) 如果关键字 key 存在于缓存中，则返回关键字的值，否则返回 -1
// - void put(int key, int value) 如果关键字 key 已经存在，则变更其数据值 value
// 如果不存在，则向缓存中插入数组 key-value。如果插入操作导致关键字量超过 capacity，则
// 应该逐出最久未使用的关键字。
// 函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
class LRUCache {
    size: number;
    capacity: number;
    map: Map<number, DLinkedNode>;
    head: DLinkedNode;
    tail: DLinkedNode;
    constructor(capacity: number) {
        this.size = 0;
        this.capacity = capacity;
        this.map = new Map();

        this.head = new DLinkedNode(-1, -1);
        this.tail = new DLinkedNode(-1, -1);
        this.head.next = this.tail;
        this.tail.prev = this.head;
    }

    get(key: number): number {
        const node = this.map.get(key);
        if (node === undefined) {
            return -1;
        }
        this.moveToHead(node);
        return node.value;
    }

    put(key: number, value: number): void {
        const node = this.map.get(key);
        if (node === undefined) {
            const newNode = new DLinkedNode(key, value);
            this.addToHead(newNode);
            this.map.set(key, newNode);
            this.size++;
            if (this.size > this.capacity) {
                const tail = this.removeTail();
                if (tail !== null) {
                    this.map.delete(tail.key);
                    this.size--;
                }
            }
        } else {
            node.value = value;
            this.moveToHead(node);
        }
    }

    moveToHead(node: DLinkedNode) {
        this.removeNode(node);
        this.addToHead(node);
    }

    addToHead(node: DLinkedNode): void {
        node.prev = this.head;
        node.next = this.head.next;
        if (this.head.next) {
            this.head.next.prev = node;
        }
        this.head.next = node;
    }

    removeNode(node: DLinkedNode): void {
        if (node.prev) {
            node.prev.next = node.next;
        }
        if (node.next) {
            node.next.prev = node.prev;
        }
    }

    removeTail(): DLinkedNode | null {
        const node = this.tail.prev;
        if (node) {
            console.log('removeTail', node);
            this.removeNode(node);
        }
        return node;
    }
}

class DLinkedNode {
    key: number;
    value: number;
    prev: DLinkedNode | null;
    next: DLinkedNode | null;
    constructor(key: number, value: number) {
        this.key = key;
        this.value = value;
        this.prev = null;
        this.next = null;
    }
}

// const cache = new LRUCache(2);
// cache.put(1, 1);
// cache.put(2, 2);
// cache.get(1);
// cache.put(3, 3);
// console.log('get(2): ', cache.get(2));

// function printLink(head: DLinkedNode | null): void {
//     const result = [];
//     while (head !== null) {
//         result.push(head.value);
//         head = head.next;
//     }
//     console.log(result.join(' -> '));
// }
